﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Xml;
using Common.Utilities;
using Common.Utilities.Dispatcher;
using Workflows.Components;
using Workflows.Components.Entities.Properties;

namespace Workflows.Components.Entities.Relations.InferredAssociations
{
    [Serializable]
    public class ClusteringComponent:WorkflowComponentBase
    {
        #region props

        private ClusteringInstruction _Instruction;
        public override InstructionBase Instruction
        {
            get { return this._Instruction; }
            set { this._Instruction=(ClusteringInstruction)value; }
        }

        private ClusteringOutput _Output;
        public override ResultBase Output
        {
            get { return this._Output; }
            set { this._Output=(ClusteringOutput)value; }
        }

        #region src
        /// <summary></summary>
        public string EntityPartitionFilePath
        {
            get
            {
                return this._Instruction.EntityPartitionFilePath;
            }
            set
            {
                this._Instruction.EntityPartitionFilePath = value;
            }
        }

        /// <summary></summary>
        public DataProviderType DbProvider
        {
            get
            {
                return this._Instruction.DbProvider;
            }
            set
            {
                this._Instruction.DbProvider = value;
            }
        }

        /// <summary></summary>
        public string ConnStr
        {
            get
            {
                return this._Instruction.ConnStr;
            }
            set
            {
                this._Instruction.ConnStr = value;
            }
        }

        /// <summary></summary>
        public string SelectSql
        {
            get
            {
                return this._Instruction.SelectSql;
            }
            set
            {
                this._Instruction.SelectSql = value;
            }
        }

        /// <summary></summary>
        public string EntityIDFieldName
        {
            get
            {
                return this._Instruction.EntityIDFieldName;
            }
            set
            {
                this._Instruction.EntityIDFieldName = value;
            }
        }
        #endregion

        #region comparer
        /// <summary></summary>
        public List<AttributeMetaData> ComparableAttributeMetaDatas
        {
            get
            {
                return this._Instruction.ComparableAttributeMetaDatas;
            }
            set
            {
                this._Instruction.ComparableAttributeMetaDatas = value;
            }
        }
        #endregion

        #region cluster settings
        /// <summary></summary>
        public string ClusterCalculatorAssemblyFileName
        {
            get
            {
                return this._Instruction.ClusterCalculatorAssemblyFileName;
            }
            set
            {
                this._Instruction.ClusterCalculatorAssemblyFileName = value;
            }
        }

        /// <summary></summary>
        public string ClusterCalculatorTypeName
        {
            get
            {
                return this._Instruction.ClusterCalculatorTypeName;
            }
            set
            {
                this._Instruction.ClusterCalculatorTypeName = value;
            }
        }

        /// <summary></summary>
        public double EntityMembershipThreshold
        {
            get
            {
                return this._Instruction.EntityMembershipThreshold;
            }
            set
            {
                this._Instruction.EntityMembershipThreshold = value;
            }
        }

        /// <summary></summary>
        public double ClusterDiameterThreshold
        {
            get
            {
                return this._Instruction.ClusterDiameterThreshold;
            }
            set
            {
                this._Instruction.ClusterDiameterThreshold = value;
            }
        }

        /// <summary></summary>
        public int ClusterSizeThreshold
        {
            get
            {
                return this._Instruction.ClusterSizeThreshold;
            }
            set
            {
                this._Instruction.ClusterSizeThreshold = value;
            }
        }

        /// <summary></summary>
        public double ClusterFuzziness
        {
            get
            {
                return this._Instruction.ClusterFuzziness;
            }
            set
            {
                this._Instruction.ClusterFuzziness = value;
            }
        }
        #endregion

        #region target
        
        /// <summary></summary>
        public DataProviderType TgtDbProvider
        {
            get
            {
                return this._Instruction.TgtDbProvider;
            }
            set
            {
                this._Instruction.TgtDbProvider = value;
            }
        }

        
        /// <summary></summary>
        public string TgtConnStr
        {
            get
            {
                return this._Instruction.TgtConnStr;
            }
            set
            {
                this._Instruction.TgtConnStr = value;
            }
        }

        
        /// <summary></summary>
        public string ClusterInstanceTableName
        {
            get
            {
                return this._Instruction.ClusterInstanceTableName;
            }
            set
            {
                this._Instruction.ClusterInstanceTableName = value;
            }
        }

        
        /// <summary></summary>
        public string ClusterInstanceClusterIDFieldName
        {
            get
            {
                return this._Instruction.ClusterInstanceClusterIDFieldName;
            }
            set
            {
                this._Instruction.ClusterInstanceClusterIDFieldName = value;
            }
        }

      
        /// <summary></summary>
        public Dictionary<string, string> ClusterInstanceFieldMappings
        {
            get
            {
                return this._Instruction.ClusterInstanceFieldMappings;
            }
            set
            {
                this._Instruction.ClusterInstanceFieldMappings = value;
            }
        }

        
        /// <summary></summary>
        public string ClusterMembershipTableName
        {
            get
            {
                return this._Instruction.ClusterMembershipTableName;
            }
            set
            {
                this._Instruction.ClusterMembershipTableName = value;
            }
        }


        /// <summary></summary>
        public string ClusterMemberClusterIDFieldName
        {
            get
            {
                return this._Instruction.ClusterMemberClusterIDFieldName;
            }
            set
            {
                this._Instruction.ClusterMemberClusterIDFieldName = value;
            }
        }


        /// <summary></summary>
        public string ClusterMemberEntityIDFieldName
        {
            get
            {
                return this._Instruction.ClusterMemberEntityIDFieldName;
            }
            set
            {
                this._Instruction.ClusterMemberEntityIDFieldName = value;
            }
        }


        /// <summary></summary>
        public string ClusterMemberScoreFieldName
        {
            get
            {
                return this._Instruction.ClusterMemberScoreFieldName;
            }
            set
            {
                this._Instruction.ClusterMemberScoreFieldName = value;
            }
        }


        /// <summary></summary>
        public string ClusterMemberMembershipFieldName
        {
            get
            {
                return this._Instruction.ClusterMemberMembershipFieldName;
            }
            set
            {
                this._Instruction.ClusterMemberMembershipFieldName = value;
            }
        }
        #endregion
        #endregion

        public ClusteringComponent():base()
        {
            this.ComponentActivityType = WorkflowActivityType.SimpleActivity;
            this.ComponentCategory = WorkflowComponentCategory.Entity_Relations.ToString();
            this.ComponentIcon = Resources.cryptui_3418;
            this.ComponentImage = Resources.doboz;
            this.ComponentName = "Clustering";
            this.Description = "group entities based on distance algorithm";

            this._Instruction =
                new ClusteringInstruction(
                    this.ComponentID, Guid.Empty,
                    string.Empty, DataProviderType.MSSQL, 
                    string.Empty, string.Empty, string.Empty,
                    new List<AttributeMetaData>(),
                    string.Empty, string.Empty, 0.5, 
                    int.MaxValue, double.PositiveInfinity, 2.0,
                    DataProviderType.MSSQL, string.Empty, 
                    string.Empty, string.Empty, new Dictionary<string, string>(),
                    string.Empty, string.Empty, string.Empty, 
                    string.Empty, string.Empty);
                                                          
            this._Output = new ClusteringOutput(this.ComponentID, Guid.Empty, 0, 0, 0, string.Empty);
        }

        public override WorkflowComponentBase Instantiate(XmlNode xNode)
        {
            if (xNode.Attributes.GetNamedItem("Type").Value == this.GetType().FullName)
            {
                ClusteringComponent wfComponent = base.Instantiate(xNode) as ClusteringComponent;

                // data source
                wfComponent.EntityPartitionFilePath = XmlDataUtil.GetAttributeValue(xNode, "EntityPartitionFilePath", "");
                wfComponent.DbProvider =
                    (DataProviderType)
                    int.Parse(XmlDataUtil.GetAttributeValue(xNode, "DbProvider",
                                                            ((int)DataProviderType.MSSQL).ToString()));
                wfComponent.ConnStr = XmlDataUtil.GetAttributeValue(xNode, "ConnStr", "");
                wfComponent.SelectSql = XmlDataUtil.GetAttributeValue(xNode, "SelectSql", "");
                wfComponent.EntityIDFieldName = XmlDataUtil.GetAttributeValue(xNode, "EntityIDFieldName", "");

                // attributes
                XmlNodeList attributeNodes = xNode.SelectNodes("Attributes/Attribute");
                if (attributeNodes != null && attributeNodes.Count > 0)
                {
                    wfComponent.ComparableAttributeMetaDatas = new List<AttributeMetaData>();
                    foreach (XmlNode attrNode in attributeNodes)
                    {
                        string assemblyName = XmlDataUtil.GetAttributeValue(attrNode, "AssemblyName", "");
                        string typeNmae = XmlDataUtil.GetAttributeValue(attrNode, "TypeName", "");
                        AttributeType attrType =
                            (AttributeType)
                            int.Parse(XmlDataUtil.GetAttributeValue(attrNode, "AttributeType",
                                                                    ((int)AttributeType.NativeAttribute).ToString()));
                        double posW = double.Parse(XmlDataUtil.GetAttributeValue(attrNode, "PositiveWeight", "1.0"));
                        double negW = double.Parse(XmlDataUtil.GetAttributeValue(attrNode, "NegativeWeight", "-1.0"));
                        bool isExternalAttr =
                            bool.Parse(XmlDataUtil.GetAttributeValue(attrNode, "IsExternal", false.ToString()));
                        DataProviderType extDbProvider =
                            (DataProviderType)
                            int.Parse(XmlDataUtil.GetAttributeValue(attrNode, "ExtDbProvider",
                                                                    ((int)DataProviderType.MSSQL).ToString()));
                        string extConnStr = XmlDataUtil.GetAttributeValue(attrNode, "ExtConnStr", "");
                        string extSql = XmlDataUtil.GetAttributeValue(attrNode, "ExtSql", "");

                        Dictionary<string, string> comparerSettings = new Dictionary<string, string>();
                        XmlNodeList settingNodes = attrNode.SelectNodes("Setting");
                        if (settingNodes != null && settingNodes.Count > 0)
                        {
                            foreach (XmlNode settingNode in settingNodes)
                            {
                                string name = XmlDataUtil.GetAttributeValue(settingNode, "Name", "");
                                string value = XmlDataUtil.GetAttributeValue(settingNode, "Value", "");
                                comparerSettings.Add(name, value);
                            }
                        }

                        if (attrType == AttributeType.CompositeAttribute)
                        {
                            Dictionary<string, string> argAttrBindings = new Dictionary<string, string>();
                            XmlNodeList attrNameNodes = attrNode.SelectNodes("Attribute");
                            foreach (XmlNode attrNameNode in attrNameNodes)
                            {
                                string argName = XmlDataUtil.GetAttributeValue(attrNameNode, "ArgumentName", "");
                                string attrName = XmlDataUtil.GetAttributeValue(attrNameNode, "AttributeName", "");
                                argAttrBindings.Add(argName, attrName);
                            }
                            
                            AttributeMetaData attrMetaData = new AttributeMetaData(
                                argAttrBindings, assemblyName, typeNmae, 
                                comparerSettings, posW, negW,
                                isExternalAttr, extDbProvider, extConnStr, extSql);
                            
                            wfComponent.ComparableAttributeMetaDatas.Add(attrMetaData);
                        }
                        else
                        {
                            string attrName = XmlDataUtil.GetAttributeValue(attrNode, "AttributeName", "");
                            AttributeMetaData attrMetaData = new AttributeMetaData(
                                attrName, attrType, assemblyName, typeNmae, 
                                comparerSettings, posW, negW,
                                isExternalAttr, extDbProvider, extConnStr, extSql);
                            wfComponent.ComparableAttributeMetaDatas.Add(attrMetaData);
                        }
                    }
                }

                // cluster 
                wfComponent.ClusterCalculatorAssemblyFileName =
                    XmlDataUtil.GetAttributeValue(xNode, "ClusterCalcAssemblyName", "");
                wfComponent.ClusterCalculatorTypeName =
                    XmlDataUtil.GetAttributeValue(xNode, "ClusterCalcTypeName", "");
                wfComponent.ClusterDiameterThreshold = double.Parse(
                    XmlDataUtil.GetAttributeValue(xNode, "ClusterDiameterThreshold", double.PositiveInfinity.ToString()));
                wfComponent.ClusterSizeThreshold = int.Parse(
                    XmlDataUtil.GetAttributeValue(xNode, "ClusterSizeThreshold", int.MaxValue.ToString()));
                wfComponent.EntityMembershipThreshold = double.Parse(
                    XmlDataUtil.GetAttributeValue(xNode, "MembershipThreshold", "0.5"));
                wfComponent.ClusterFuzziness = double.Parse(
                    XmlDataUtil.GetAttributeValue(xNode, "Fuzziness", "2.0"));

                // tgt 
                wfComponent.TgtDbProvider =
                    (DataProviderType)int.Parse(
                                           XmlDataUtil.GetAttributeValue(
                                               xNode, "TgtDbProvider", ((int)DataProviderType.MSSQL).ToString()));
                wfComponent.TgtConnStr = XmlDataUtil.GetAttributeValue(xNode, "TgtConnStr", "");
                wfComponent.ClusterInstanceTableName = XmlDataUtil.GetAttributeValue(xNode, "InstanceTable", "");
                wfComponent.ClusterInstanceClusterIDFieldName =
                    XmlDataUtil.GetAttributeValue(xNode, "InstanceClusterIDFieldName", "");
                XmlNodeList fieldMappingNodes = xNode.SelectNodes("InstanceFieldMappings/FieldMapping");
                if (fieldMappingNodes != null && fieldMappingNodes.Count > 0)
                {
                    wfComponent.ClusterInstanceFieldMappings = new Dictionary<string, string>();
                    foreach (XmlNode mappingNode in fieldMappingNodes)
                    {
                        string srcFieldName = XmlDataUtil.GetAttributeValue(mappingNode, "SrcField", "");
                        string tgtFieldName = XmlDataUtil.GetAttributeValue(mappingNode, "TgtField", "");
                        wfComponent.ClusterInstanceFieldMappings.Add(srcFieldName, tgtFieldName);
                    }
                }
                wfComponent.ClusterMembershipTableName = XmlDataUtil.GetAttributeValue(xNode, "MemberTable", "");
                wfComponent.ClusterMemberClusterIDFieldName =
                    XmlDataUtil.GetAttributeValue(xNode, "MemberClusterIDField", "");
                wfComponent.ClusterMemberEntityIDFieldName =
                    XmlDataUtil.GetAttributeValue(xNode, "MemberEntityIDField", "");
                wfComponent.ClusterMemberScoreFieldName =
                    XmlDataUtil.GetAttributeValue(xNode, "MemberScoreField", "");
                wfComponent.ClusterMemberMembershipFieldName =
                    XmlDataUtil.GetAttributeValue(xNode, "MemberMembershipField", "");

                return wfComponent;
            }
            else
            {
                throw new Exception("Unable to instantiate component: invalid type specified in xml node");
            }
        }

        public override XmlNode Serialize(ref XmlDocument xDoc, ref XmlNode parentNode)
        {
            XmlNode xNode = base.Serialize(ref xDoc, ref parentNode);
            // src
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "EntityPartitionFilePath", this.EntityPartitionFilePath);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "DbProvider", ((int)this.DbProvider).ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "ConnStr", this.ConnStr);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "SelectSql", this.SelectSql);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "EntityIDFieldName", this.EntityIDFieldName);

            // comparers 
            if (this.ComparableAttributeMetaDatas != null && this.ComparableAttributeMetaDatas.Count > 0)
            {
                XmlNode attrRootNode = XmlDataUtil.AddElement(ref xDoc, xNode, "Attributes");
                foreach (AttributeMetaData attributeMetaData in this.ComparableAttributeMetaDatas)
                {
                    XmlNode attrNode = XmlDataUtil.AddElement(ref xDoc, attrRootNode, "Attribute");
                    XmlDataUtil.UpdateAttribute(ref xDoc, attrNode, "AssemblyName", attributeMetaData.ComparerAssemblyName);
                    XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "TypeName", attributeMetaData.ComparerTypeName);
                    XmlDataUtil.UpdateAttribute(ref xDoc, attrNode, "AttributeType",
                                                ((int)attributeMetaData.AttributeType).ToString());
                    XmlDataUtil.UpdateAttribute(ref xDoc, attrNode, "PositiveWeight", attributeMetaData.PositiveMatchWeight.ToString());
                    XmlDataUtil.UpdateAttribute(ref xDoc, attrNode, "NegativeWeight", attributeMetaData.NegativeMatchWeight.ToString());
                    XmlDataUtil.UpdateAttribute(ref xDoc, attrNode, "IsExternal", attributeMetaData.IsExternalAttribute.ToString());
                    XmlDataUtil.UpdateAttribute(ref xDoc, attrNode, "ExtDbProvider", ((int)attributeMetaData.ExternalAttributeDbProvider).ToString());
                    XmlDataUtil.UpdateAttribute(ref xDoc, attrNode, "ExtConnStr", attributeMetaData.ExternalAttributeConnStr);
                    XmlDataUtil.UpdateAttribute(ref xDoc, attrNode, "ExtSql", attributeMetaData.ExternalAttributeSelectSql);

                    if (attributeMetaData.ComparerSettings != null && attributeMetaData.ComparerSettings.Count > 0)
                    {
                        foreach (string name in attributeMetaData.ComparerSettings.Keys)
                        {
                            XmlNode settingNode = XmlDataUtil.AddElement(ref xDoc, attrNode, "Setting");
                            XmlDataUtil.UpdateAttribute(ref xDoc, settingNode, "Name", name);
                            XmlDataUtil.UpdateAttribute(ref xDoc, settingNode, "Value", attributeMetaData.ComparerSettings[name]);
                        }
                    }

                    if (attributeMetaData.AttributeType == AttributeType.CompositeAttribute)
                    {
                        
                        foreach (string argName in attributeMetaData.ArgNameAttrNameBindings.Keys)
                        {
                            XmlNode attrNameNode = XmlDataUtil.AddElement(ref xDoc, attrNode, "Attribute");
                            XmlDataUtil.UpdateAttribute(ref xDoc, attrNameNode, "ArgumentName", argName);
                            XmlDataUtil.UpdateAttribute(ref xDoc, attrNameNode, "AttributeName",
                                                        attributeMetaData.ArgNameAttrNameBindings[argName]);
                        }
                    }
                    else
                    {
                        XmlDataUtil.UpdateAttribute(ref xDoc, attrNode, "AttributeName", attributeMetaData.AttributeName);
                    }
                }
            }

            // cluster 
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "ClusterCalcAssemblyName",
                                        this.ClusterCalculatorAssemblyFileName);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "ClusterCalcTypeName", this.ClusterCalculatorTypeName);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "ClusterDiameterThreshold",
                                        this.ClusterDiameterThreshold.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "ClusterSizeThreshold", this.ClusterSizeThreshold.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "MembershipThreshold",
                                        this.EntityMembershipThreshold.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "Fuzziness", this.ClusterFuzziness.ToString());

            // tgt 
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "TgtDbProvider", ((int)this.TgtDbProvider).ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "TgtConnStr", this.TgtConnStr);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "InstanceTable", this.ClusterInstanceTableName);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "InstanceClusterIDField", this.ClusterInstanceClusterIDFieldName);
            if (this.ClusterInstanceFieldMappings != null && this.ClusterInstanceFieldMappings.Count > 0)
            {
                XmlNode fieldMappingRootNode = XmlDataUtil.AddElement(ref xDoc, xNode, "InstanceFieldMappings");
                foreach (string srcField in this.ClusterInstanceFieldMappings.Keys)
                {
                    XmlNode fieldMappingNode = XmlDataUtil.AddElement(ref xDoc, fieldMappingRootNode, "FieldMapping");
                    XmlDataUtil.UpdateAttribute(ref xDoc, fieldMappingNode, "SrcField", srcField);
                    XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "TgtField", this.ClusterInstanceFieldMappings[srcField]);
                }
            }
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "MemberTable", this.ClusterMembershipTableName);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "MemberClusterIDField", this.ClusterMemberClusterIDFieldName);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "MemberEntityIDField", this.ClusterMemberEntityIDFieldName);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "MemberScoreField", this.ClusterMemberScoreFieldName);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "MemberMembershipField", this.ClusterMemberMembershipFieldName);


            return xNode;
        }
    }
}
